from django.utils import timezone
from django.utils.datetime_safe import datetime
from decimal import Decimal

from django_redis import get_redis_connection
from rest_framework import serializers
from django.db import transaction

from goods.models import SKU
from orders.models import OrderInfo, OrderGoods


class CartSKUSerializer(serializers.ModelSerializer):
    """订单中的商品序列化器"""
    count = serializers.IntegerField(label='购买数量')

    class Meta:
        model = SKU
        fields = ['id', 'name', 'price', 'default_image_url', 'count']


class OrderSettlementSerializer(serializers.Serializer):
    """订单序列化器"""

    skus = CartSKUSerializer(many=True)
    freight = serializers.DecimalField(label='运费', max_digits=10, decimal_places=2)


class CommitOrderSerializer(serializers.ModelSerializer):
    """保存订单序列化器"""

    class Meta:
        model = OrderInfo
        fields = ['address', 'pay_method', 'order_id']
        read_only_fields = ['order_id']
        extra_kwargs = {
            'address': {'write_only': True},
            'pay_method': {'write_only': True},
        }

    def create(self, validated_data):
        """需要在这里操作4张表"""

        # 获取当前下单用户
        user = self.context.get('request').user
        # 获取前端传入的收货地址
        address = validated_data.get('address')
        # 获取前端传入的支付方式
        pay_method = validated_data.get('pay_method')
        # 订单状态
        status = OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY'] \
            else OrderInfo.ORDER_STATUS_ENUM['UNSEND']
        # 生成订单编号
        order_id = timezone.now().strftime('%Y%m%d%H%M%S') + '%09d' % user.id
        with transaction.atomic():
            # 创建事务保存点
            save_point = transaction.savepoint()
            try:
                # 保存订单基本信息数据 OrderInfo
                order_info = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    # TODO 商品总数暂时获取不到，先存0
                    total_count=0,
                    # TODO 商品总价时获取不到，先存0
                    total_amount=Decimal('0.00'),
                    freight=Decimal('10.00'),
                    status=status,
                    pay_method=pay_method,
                )
                # 从redis中获取购物车结算商品数据
                redis_conn = get_redis_connection('cart')
                cart_dict_redis = redis_conn.hgetall('cart_%s' % user.id)
                selected_ids = redis_conn.smembers('selected_%s' % user.id)
                # 遍历结算商品：
                # 这里不要用 SKU.objects.filter(id__in=selected_ids) queryset两大特性:1 惰性,2.缓存, 这样查询可能存在缓存
                for sku_id_bytes in selected_ids:
                    while True:  # 让用户对同一个商品有无限次下单机会
                        sku = SKU.objects.get(id=sku_id_bytes)

                        # 获取当前商品的购买数量
                        buy_count = int(cart_dict_redis[sku_id_bytes])
                        # 获取SKU的库存跟销量
                        origin_sales = sku.sales  # 当前购买商品的原有销量
                        origin_stock = sku.stock  # 当前购买商品的原库存
                        # 判断商品库存是否充足
                        if buy_count > origin_stock:
                            raise serializers.ValidationError('库存不足')
                        # 减少库存,增加销量 SKU
                        new_sales = origin_sales + buy_count
                        new_stock = origin_stock - buy_count
                        # sku.sales = new_sales
                        # sku.stock = new_stock
                        # sku.save()
                        result = SKU.objects.filter(stock=origin_stock, id=sku_id_bytes).update(sales=new_sales, stock=new_stock)
                        if result == 0:  # 如果没有修改成功, 说明有资源抢夺
                            # raise serializers.ValidationError('存在资源抢夺')
                            continue
                        # 修改当前购买商品的SPU的销量
                        spu = sku.goods
                        spu.sales += new_sales
                        spu.save()
                        # 保存订单商品数据
                        OrderGoods.objects.create(
                            order=order_info,
                            sku=sku,
                            count=buy_count,
                            price=sku.price
                        )

                        # 累加计算总数量和总和
                        order_info.total_count += buy_count
                        order_info.total_amount += (sku.price * buy_count)

                        break  # 当前商品下单成功, 跳出死循环
                # 最后加入邮费和保存订单信息
                order_info.total_amount += order_info.freight
                order_info.save()
            except Exception as e:
                # 进行暴力回滚
                print(e)
                transaction.savepoint_rollback(save_point)
                raise serializers.ValidationError('库存不足')
            else:
                transaction.savepoint_commit(save_point)
        # 在redis购物车中删除已计算商品数据
        pl = redis_conn.pipeline()
        pl.hdel('cart_%s' % user.id, *selected_ids)
        pl.srem('selected_%s' % user.id, *selected_ids)
        pl.execute()
        return order_info
